#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2023, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
#--------------------------------------------------------------------------- #

require 'fileutils'
require 'tmpdir'

VERSION = '6.9.80'

def version
    v = VERSION

    _, min, incr = v.split('.').collect {|e| e.to_i }

    if min >= 80 || incr >= 80
        v += '.pre'
    end

    v
end

DEFAULTS={
    :version => version,
    :date => Time.now.strftime('%Y-%m-%d'),
    :dependencies => []
}

TEMPLATE=<<~EOT
    # This file is automatically generated

    Gem::Specification.new do |s|
      s.name        = '$(NAME)'
      s.version     = '$(VERSION)'
      s.date        = '$(DATE)'
      s.summary     = "$(SUMMARY)"
      s.description = "$(DESCRIPTION)"
      s.authors     = ["OpenNebula"]
      s.email       = 'contact@opennebula.io'
      s.license     = 'Apache-2.0'
      s.files       = [
    $(FILES)
      ]
      s.homepage    = 'http://opennebula.io'
    $(EXECUTABLES)
    $(DEPENDENCIES)
    end

EOT

GEM_DESCRIPTION={
    :oca => {
        :name => 'opennebula',
        :files => [
            ['src/oca/ruby/opennebula.rb', '/lib'],

            # Needed for onevcenter
            # TODO: respect source dir. hierarchy, this is terrible mess!
            ['src/vmm_mad/remotes/vcenter/vcenter_driver.rb', '/lib'],
            ['src/vmm_mad/remotes/lib/vcenter_driver/**/*.rb', '/lib'],
            ['src/vmm_mad/remotes/nsx/nsx_driver.rb', '/lib'],
            ['src/vmm_mad/remotes/lib/nsx_driver/*.rb', '/lib'],

            # vcenter_driver.rb dependencies
            ['src/mad/ruby/VirtualMachineDriver.rb', '/lib'],
            ['src/mad/ruby/OpenNebulaDriver.rb', '/lib'],
            ['src/mad/ruby/CommandManager.rb', '/lib'],
            ['src/mad/ruby/ActionManager.rb', '/lib'],
            ['src/mad/ruby/HostSyncManager.rb', '/lib'],
            ['src/mad/ruby/DriverExecHelper.rb', '/lib'],
            ['src/mad/ruby/scripts_common.rb', '/lib'],

            # other depenencies
            ['src/flow/lib/models.rb', '/lib'],
            ['src/flow/lib/models/*.rb', '/lib/models'],

            ['src/oca/ruby/opennebula/*.rb', '/lib/opennebula'],
            ['src/oca/ruby/opennebula/flow/*.rb', '/lib/opennebula/flow'],
            ['src/authm_mad/remotes/**/*.rb', '/lib/opennebula'],
            ['src/cloud/common/CloudClient.rb', '/lib/cloud'],
            ['NOTICE', ''],
            ['LICENSE', '']
        ],
        :summary => 'OpenNebula Client API',
        :description => 'Libraries needed to talk to OpenNebula',
        :dependencies => [
            ['nokogiri','<1.16'],
            'json',
            'rbvmomi2',
            'xmlrpc',
            'treetop',    # oneflow
            'ipaddress',  # oneflow
            'parse-cron'  # oneflow
        ]
    },

    :cli => {
        :name => 'opennebula-cli',
        :files => [
            # Needed for JSON and YAML outputs
            ['share/doc/xsd/*.xsd', '/share/schemas/xsd'],

            ['src/cli/one[a-z]*', '/bin', 'one(provider|provision)'],
            ['src/cli/*.rb', '/lib'],
            ['src/cli/one_helper/*.rb', '/lib/one_helper', 'one(provider|provision)'],
            ['NOTICE', ''],
            ['LICENSE', '']
        ],
        :summary => 'OpenNebula Command Line Interface',
        :description => 'Commands used to talk to OpenNebula',
        :dependencies => [
            ['opennebula', "= #{DEFAULTS[:version]}"],
            'activesupport'
        ]
    }
}

def sane_prefix(prefix)
    return '' if !prefix or prefix.empty?

    p=prefix

    p.slice!(0) if p[0, 1]=='/'
    p<<'/' if p[-1, 1]!='/'

    p
end

def file_list(description)
    files=[]

    description[:files].each do |f, prefix, exclude|
        Dir.glob(f).each do |source|
            next if !exclude.nil? and source.match(exclude)

            files << [source, sane_prefix(prefix)+File.basename(source)]
        end
    end

    files
end

def copy_files(files, source_prefix = '', destination_prefix = '')
    files.each do |file|
        source=source_prefix+file[0]
        destination=destination_prefix+file[1]

        dir=File.dirname destination

        FileUtils.mkdir_p(dir) unless File.exist?(dir)

        FileUtils.cp(source, destination)
    end
end

def generate_gem_file_list(files)
    files.map do |f|
        "    '#{f.last}'"
    end.join(",\n")
end

def generate_gem_executable_list(files)
    executables=files
                .select {|f| f.last.match(%r{^bin/}) }
                .map {|f| "'#{File.basename(f.last)}'" }
                .join(', ')

    if !executables.empty?
        "  s.executables=[#{executables}]"
    else
        nil
    end
end

def generate_dependencies(dependencies)
    dependencies.map do |d|
        line='  s.add_runtime_dependency '
        if d.is_a?(Array)
            line<<d.map {|part| "'#{part}'" }.join(', ')
        else
            line<<"'#{d}'"
        end
        line
    end.join("\n")
end

def populate_gem_template(description, files)
    reg=/\$\((\w+)\)/
    TEMPLATE.gsub(reg) do |chunk|
        m=chunk.match(reg)
        var=m[1].downcase.to_sym

        if var==:files
            generate_gem_file_list(files)
        elsif var==:dependencies
            generate_dependencies(
                description[:dependencies]||DEFAULTS[:dependencies]
            )
        elsif var==:executables
            generate_gem_executable_list(files)
        elsif description[var]
            description[var]
        else
            DEFAULTS[var]
        end
    end
end

def generate_gem_file(gem_file, description, files)
    File.open(gem_file, 'w') do |f|
        f.write(populate_gem_template(description, files))
    end
end

def generate_gem(description)
    Dir.mktmpdir do |tmp|
        files=file_list(description)
        copy_files(files, '', tmp+'/')

        gem_file=tmp+'/gem.gemspec'
        generate_gem_file(gem_file, description, files)

        pwd=Dir.pwd

        Dir.chdir(tmp) do
            system('gem build gem.gemspec')

            if $?.exitstatus!=0
                puts 'Error generating gem'
                exit(-1)
            end

            Dir.glob('*.gem').each do |f|
                FileUtils.cp(f, pwd)
            end
        end
    end
end

# Go to source code root dir
root=Dir.pwd
root.gsub!(%r{/share/rubygems$}, '')
Dir.chdir(root)

GEM_DESCRIPTION.each {|_name, gem| generate_gem(gem) }
